<?php
/**
 * Created by PhpStorm.
 * Script Name: Index.php
 * Create: 2023/5/23 11:03
 * Description:
 * Author: fudaoji<fdj@kuryun.cn>
 */

namespace addons\fhelper\admin\controller;

use addons\fhelper\common\library\FmConfig;
use addons\fhelper\common\library\FmZipper;
use addons\fhelper\common\library\FmZipperTar;

class Index extends Base
{
    /**
     * 文件列表
     * @return mixed|void
     * Author: fudaoji<fdj@kuryun.cn>
     */
    public function index()
    {
        $params = input();
        if(!empty($params['group']) && !empty($params['delete'])){
            return $this->massDel();
        }
        if(!empty($params['group']) && (!empty($params['zip']) || !empty($params['tar']))){
            return $this->pack();
        }

        // check path
        if (!is_dir($this->path)) {
            $this->redirect(FM_SELF_URL . '?p=');
        }

        /*$objects = is_readable($path) ? scandir($path) : [];
        $folders = [];
        $files = [];
        $current_path = array_slice(explode("/", $path), -1)[0];
        if (is_array($objects) && fm_is_exclude_items($current_path)) {
            foreach ($objects as $file) {
                if ($file == '.' || $file == '..') {
                    continue;
                }
                if (!FM_SHOW_HIDDEN && substr($file, 0, 1) === '.') {
                    continue;
                }
                $new_path = $path . '/' . $file;
                if (@is_file($new_path) && fm_is_exclude_items($file)) {
                    $files[] = $file;
                } elseif (@is_dir($new_path) && $file != '.' && $file != '..' && fm_is_exclude_items($file)) {
                    $folders[] = $file;
                }
            }
        }

        if (!empty($files)) {
            natcasesort($files);
        }
        if (!empty($folders)) {
            natcasesort($folders);
        }*/
        $num_files = count($this->files);
        $num_folders = count($this->folders);
        $all_files_size = 0;
        $tableTheme = (FM_THEME == "dark") ? "text-white bg-dark table-dark" : "bg-white";

        $assign = compact(
            'num_files', 'num_folders', 'all_files_size', 'tableTheme'
        );
        return $this->show($assign);
    }

    /**
     * 修改权限
     * @return mixed|void
     * Author: fudaoji<fdj@kuryun.cn>
     */
    public function chmod(){
        $params = input();
        if($this->request->isPost()){
            $check = $this->request->checkToken('__token__');
            if (false === $check) {
                fm_set_msg(lng("Invalid Token."), 'danger');
                $this->redirect($this->url('index', ['p' => FM_PATH]));
            }

            $path = FM_ROOT_PATH;
            if (FM_PATH != '') {
                $path .= '/' . FM_PATH;
            }

            $file = $params['chmod'];
            $file = fm_clean_path($file);
            $file = str_replace('/', '', $file);
            if ($file == '' || (!is_file($path . '/' . $file) && !is_dir($path . '/' . $file))) {
                fm_set_msg(lng('File not found'), 'danger');
                $this->redirect($this->url('index', ['p' => FM_PATH]));
            }

            $mode = 0;
            if (!empty($_POST['ur'])) {
                $mode |= 0400;
            }
            if (!empty($_POST['uw'])) {
                $mode |= 0200;
            }
            if (!empty($_POST['ux'])) {
                $mode |= 0100;
            }
            if (!empty($_POST['gr'])) {
                $mode |= 0040;
            }
            if (!empty($_POST['gw'])) {
                $mode |= 0020;
            }
            if (!empty($_POST['gx'])) {
                $mode |= 0010;
            }
            if (!empty($_POST['or'])) {
                $mode |= 0004;
            }
            if (!empty($_POST['ow'])) {
                $mode |= 0002;
            }
            if (!empty($_POST['ox'])) {
                $mode |= 0001;
            }

            if (@chmod($path . '/' . $file, $mode)) {
                fm_set_msg(lng('Permissions changed'));
            } else {
                fm_set_msg(lng('Permissions not changed'), 'danger');
            }
            $this->redirect($this->url('index', ['p' => FM_PATH]));
        }
        $path = $this->path;
        $file = $params['chmod'];
        $file = fm_clean_path($file);
        $file = str_replace('/', '', $file);
        if ($file == '' || (!is_file($path . '/' . $file) && !is_dir($path . '/' . $file))) {
            fm_set_msg(lng('File not found'), 'danger');
            $this->redirect($this->url('index', ['p' => FM_PATH]));
        }

        $file_url = FM_ROOT_URL . (FM_PATH != '' ? '/' . FM_PATH : '') . '/' . $file;
        $file_path = $path . '/' . $file;
        $mode = fileperms($path . '/' . $file);
        $assign = compact('file_path', 'file_url', 'mode', 'file');
        return $this->show($assign);
    }

    /**
     * Unpack解压
     * Author: fudaoji<fdj@kuryun.cn>
     */
    public function unpack(){
        $check = $this->request->checkToken('__token__');
        if (false === $check) {
            fm_set_msg(lng("Invalid Token."), 'danger');
            $this->redirect($_SERVER['HTTP_REFERER']);
        }
        $params = input();
        $unzip = urldecode($params['unzip']);
        $unzip = fm_clean_path($unzip);
        $unzip = str_replace('/', '', $unzip);
        $isValid = false;

        $path = FM_ROOT_PATH;
        if (FM_PATH != '') {
            $path .= '/' . FM_PATH;
        }

        $zip_path = '';
        $ext = '';

        if ($unzip != '' && is_file($path . '/' . $unzip)) {
            $zip_path = $path . '/' . $unzip;
            $ext = pathinfo($zip_path, PATHINFO_EXTENSION);
            $isValid = true;
        } else {
            fm_set_msg(lng('File not found'), 'danger');
            $this->redirect($_SERVER['HTTP_REFERER']);
        }

        if (($ext == "zip" && !class_exists('ZipArchive')) || ($ext == "tar" && !class_exists('PharData'))) {
            fm_set_msg(lng('Operations with archives are not available'), 'danger');
            $this->redirect($_SERVER['HTTP_REFERER']);
        }

        if ($isValid) {
            //to folder
            $tofolder = '';
            if (isset($params['tofolder'])) {
                $tofolder = pathinfo($zip_path, PATHINFO_FILENAME);
                if (fm_mkdir($path . '/' . $tofolder, true)) {
                    $path .= '/' . $tofolder;
                }
            }

            try {
                if($ext == "zip") {
                    $zipper = new FmZipper();
                    $zipper->unzip($zip_path, $path);
                } elseif ($ext == "tar") {
                    $gzipper = new \PharData($zip_path);
                    if (! @$gzipper->extractTo($path,null, true)) {
                        fm_set_msg(lng('Unpacked Fail'), 'danger');
                        $this->redirect($_SERVER['HTTP_REFERER']);
                    }
                }
                fm_set_msg(lng('Unpacked Successful'));
            } catch (\Exception $e) {
                fm_set_msg($e->getMessage(), 'danger');
                $this->redirect($_SERVER['HTTP_REFERER']);
            }
        } else {
            fm_set_msg(lng('File not found'), 'danger');
        }
        $this->redirect($this->url('index', ['p' => FM_PATH]));
    }

    /**
     * 备份文件
     * Author: fudaoji<fdj@kuryun.cn>
     */
    public function backup(){
        $params = input();
        $fileName = fm_clean_path($params['file']);
        $fullPath = FM_ROOT_PATH . '/';
        if (!empty($params['path'])) {
            $relativeDirPath = fm_clean_path($params['path']);
            $fullPath .= "{$relativeDirPath}".DS;
        }
        $date = date("dMy-His");
        $newFileName = "{$fileName}-{$date}.bak";
        $fullyQualifiedFileName = $fullPath . $fileName;
        try {
            if (!file_exists($fullyQualifiedFileName)) {
                return json(['code' => 0, 'msg' => lng("File {$fileName} not found")]);
            }
            if (copy($fullyQualifiedFileName, $fullPath . $newFileName)) {
                return json(['code' => 1, 'msg' => lng("Backup {$newFileName} created")]);
            } else {
                return json(['code' => 0, 'msg' => lng("Could not copy file {$fileName}")]);
            }
        } catch (\Exception $e) {
            return json(['code' => 0, 'msg' => '系统错误：'.$e->getMessage()]);
        }
    }

    /**
     * 编辑
     * @return mixed|void
     * Author: fudaoji<fdj@kuryun.cn>
     */
    public function edit(){
        // file editor
        $params = input();
        $path = $this->path;
        $file = $params['edit'];
        $file = fm_clean_path($file, false);
        $file = str_replace('/', '', $file);
        if ($file == '' || !is_file($path . '/' . $file) || in_array($file, $this->configs['exclude_items'])) {
            fm_set_msg(lng('File not found'), 'danger');
            $this->redirect($this->url('index', ['p' => FM_PATH]));
        }
        $editFile = ' : <i><b>'. $file. '</b></i>';
        header('X-XSS-Protection:0');

        $file_url = FM_ROOT_URL . fm_convert_win((FM_PATH != '' ? '/' . FM_PATH : '') . '/' . $file);
        $file_path = $path . '/' . $file;

        // normal editer
        $isNormalEditor = true;
        if (isset($params['env'])) {
            if ($params['env'] == "ace") {
                $isNormalEditor = false;
            }
        }

        // Save File
        if (isset($params['savedata'])) {
            $writedata = $params['savedata'];
            $fd = fopen($file_path, "w");
            @fwrite($fd, $writedata);
            fclose($fd);
            fm_set_msg(lng('File Saved Successfully'));
        }elseif (!empty($params['type']) && $params['type'] == 'save'){
            $file_path = $path . '/' . $file;
            $writedata = $params['content'];
            $fd = fopen($file_path, "w");
            $write_results = @fwrite($fd, $writedata);
            fclose($fd);
            if ($write_results === false){
                header("HTTP/1.1 500 Internal Server Error");
                die("Could Not Write File! - Check Permissions / Ownership");
            }
            die(true);
        }

        $ext = strtolower(pathinfo($file_path, PATHINFO_EXTENSION));
        $mime_type = fm_get_mime_type($file_path);
        $filesize = filesize($file_path);
        $is_text = false;
        $content = ''; // for text

        if (in_array($ext, fm_get_text_exts()) || substr($mime_type, 0, 4) == 'text' || in_array($mime_type, fm_get_text_mimes())) {
            $is_text = true;
            $content = file_get_contents($file_path);
        }
        $ext = pathinfo($file, PATHINFO_EXTENSION);
        $ext =  $ext == "js" ? "javascript" :  $ext;

        $assign = compact('content', 'is_text', 'filesize', 'isNormalEditor', 'editFile',
            'ext', 'file', 'file_url');
        return $this->show($assign);
    }

    /**
     * 文件查看
     * @return mixed|void
     * Author: fudaoji<fdj@kuryun.cn>
     */
    public function detail(){
        // Online office Docs Viewer
        // Availabe rules are 'google', 'microsoft' or false
        // Google => View documents using Google Docs Viewer
        // Microsoft => View documents using Microsoft Web Apps Viewer
        // false => disable online doc viewer
        $online_viewer = 'microsoft';
        defined('FM_DOC_VIEWER') || define('FM_DOC_VIEWER', $online_viewer);

        // Enable highlight.js (https://highlightjs.org/) on view's page
        $use_highlightjs = true;

        // highlight.js style
        // for dark theme use 'ir-black'
        $highlightjs_style = 'vs';
        defined('FM_USE_HIGHLIGHTJS') || define('FM_USE_HIGHLIGHTJS', $use_highlightjs);
        defined('FM_HIGHLIGHTJS_STYLE') || define('FM_HIGHLIGHTJS_STYLE', $highlightjs_style);

        // Enable ace.js (https://ace.c9.io/) on view's page
        $edit_files = true;
        define('FM_EDIT_FILE', $edit_files);


        $exclude_items = $this->configs['exclude_items'];
        $path = $this->path;
        $params = input();
        $file = $params['f'];
        $file = fm_clean_path($file, false);
        $file = str_replace('/', '', $file);
        if ($file == '' || !is_file($path . '/' . $file) || in_array($file, $exclude_items)) {
            fm_set_msg(lng('File not found'), 'danger');
            $this->redirect($this->url('index', ['p' => FM_PATH]));
        }

        $file_url = FM_ROOT_URL . fm_convert_win((FM_PATH != '' ? '/' . FM_PATH : '') . '/' . $file);
        $file_path = $path . '/' . $file;

        $ext = strtolower(pathinfo($file_path, PATHINFO_EXTENSION));
        $mime_type = fm_get_mime_type($file_path);
        $filesize_raw = fm_get_size($file_path);
        $filesize = fm_get_filesize($filesize_raw);

        $is_zip = false;
        $is_gzip = false;
        $is_image = false;
        $is_audio = false;
        $is_video = false;
        $is_text = false;
        $is_onlineViewer = false;

        $view_title = 'File';
        $filenames = false; // for zip
        $content = ''; // for text
        $online_viewer = strtolower(FM_DOC_VIEWER);

        if($online_viewer && $online_viewer !== 'false' && in_array($ext, fm_get_onlineViewer_exts())){
            $is_onlineViewer = true;
        } elseif ($ext == 'zip' || $ext == 'tar') {
            $is_zip = true;
            $view_title = 'Archive';
            $filenames = fm_get_zif_info($file_path, $ext);
        } elseif (in_array($ext, fm_get_image_exts())) {
            $is_image = true;
            $view_title = 'Image';
        } elseif (in_array($ext, fm_get_audio_exts())) {
            $is_audio = true;
            $view_title = 'Audio';
        } elseif (in_array($ext, fm_get_video_exts())) {
            $is_video = true;
            $view_title = 'Video';
        } elseif (in_array($ext, fm_get_text_exts()) || substr($mime_type, 0, 4) == 'text' || in_array($mime_type, fm_get_text_mimes())) {
            $is_text = true;
            $content = file_get_contents($file_path);
        }

        $assign = compact('filenames', 'view_title', 'file', 'file_path', 'filesize_raw',
            'filesize', 'mime_type', 'is_zip', 'is_gzip', 'is_audio', 'is_image', 'is_text', 'is_video', 'is_onlineViewer',
            'content', 'file_url', 'online_viewer', 'exclude_items', 'use_highlightjs', 'ext');
        return $this->show($assign);
    }

    /**
     * Mass copy files/ folders 批量复制
     * @return mixed|void
     * Author: fudaoji<fdj@kuryun.cn>
     */
    public function massCopy(){
        $params = input();
        if(!empty($params['copy_to']) && !empty($params['finish'])){
            $check = $this->request->checkToken('__token__');
            if (false === $check) {
                fm_set_msg(lng("Invalid Token."), 'danger');
                $this->redirect($this->url('index', ['p' => FM_PATH]));
            }

            // from
            $path = FM_ROOT_PATH;
            if (FM_PATH != '') {
                $path .= '/' . FM_PATH;
            }
            // to
            $copy_to_path = FM_ROOT_PATH;
            $copy_to = fm_clean_path($_POST['copy_to']);
            if ($copy_to != '') {
                $copy_to_path .= '/' . $copy_to;
            }
            if ($path == $copy_to_path) {
                fm_set_msg(lng('Paths must be not equal'), 'warning');
                $this->redirect($this->url('index', ['p' => FM_PATH]));
            }
            if (!is_dir($copy_to_path)) {
                if (!fm_mkdir($copy_to_path, true)) {
                    fm_set_msg('Unable to create destination folder', 'danger');
                    $this->redirect($this->url('index', ['p' => FM_PATH]));
                }
            }

            // move?
            $move = isset($params['move']);
            // copy/move
            $errors = 0;
            $files = $params['file'];
            if (is_array($files) && count($files)) {
                foreach ($files as $f) {
                    if ($f != '') {
                        $f = fm_clean_path($f);
                        // abs path from
                        $from = $path . '/' . $f;
                        // abs path to
                        $dest = $copy_to_path . '/' . $f;
                        // do
                        if ($move) {
                            $rename = fm_rename($from, $dest);
                            if ($rename === false) {
                                $errors++;
                            }
                        } else {
                            if (!fm_rcopy($from, $dest)) {
                                $errors++;
                            }
                        }
                    }
                }
                if ($errors == 0) {
                    $msg = $move ? 'Moved Successful' : 'Copied Successful';
                    fm_set_msg(lng($msg));
                } else {
                    $msg = $move ? 'Error while moving items' : 'Error while copying items';
                    fm_set_msg(lng($msg), 'danger');
                }
            } else {
                fm_set_msg(lng('Nothing selected'), 'warning');
            }
            $this->redirect($this->url('index', ['p' => FM_PATH]));
        }

        $copy_files = isset($_POST['file']) ? $_POST['file'] : null;
        if (!is_array($copy_files) || empty($copy_files)) {
            fm_set_msg(lng('Nothing selected'), 'warning');
            $this->redirect($this->url('index', ['p' => FM_PATH]));
        }
        $assign = compact('copy_files');
        return $this->show($assign);
    }

    /**
     * Pack files zip, tar
     * Author: fudaoji<fdj@kuryun.cn>
     */
    private function pack(){
        $check = $this->request->checkToken('__token__');
        if (false === $check) {
            fm_set_msg(lng("Invalid Token."), 'danger');
            $this->redirect($this->url('index', ['p' => FM_PATH]));
        }
        $params = input();
        $path = FM_ROOT_PATH;
        $ext = 'zip';
        if (FM_PATH != '') {
            $path .= '/' . FM_PATH;
        }

        //set pack type
        $ext = isset($params['tar']) ? 'tar' : 'zip';
        if (($ext == "zip" && !class_exists('ZipArchive')) || ($ext == "tar" && !class_exists('PharData'))) {
            fm_set_msg(lng('Operations with archives are not available'), 'danger');
            $this->redirect($this->url('index', ['p' => FM_PATH]));
        }

        $files = $params['file'];
        $sanitized_files = array();

        // clean path
        foreach($files as $file){
            array_push($sanitized_files, fm_clean_path($file));
        }

        $files = $sanitized_files;

        if (!empty($files)) {
            chdir($path);
            if (count($files) == 1) {
                $one_file = reset($files);
                $one_file = basename($one_file);
                $zipname = $one_file . '_' . date('ymd_His') . '.'.$ext;
            } else {
                $zipname = 'archive_' . date('ymd_His') . '.'.$ext;
            }

            if($ext == 'zip') {
                $zipper = new FmZipper();
                $res = $zipper->create($zipname, $files);
            } elseif ($ext == 'tar') {
                $tar = new FmZipperTar();
                $res = $tar->create($zipname, $files);
            }

            if ($res) {
                fm_set_msg(sprintf(lng('Archive').' <b>%s</b> '.lng('Created'), fm_enc($zipname)));
            } else {
                fm_set_msg(lng('Archive not created'), 'danger');
            }
        } else {
            fm_set_msg(lng('Nothing selected'), 'warning');
        }
        $this->redirect($this->url('index', ['p' => FM_PATH]));
    }

    /**
     * 批量删除
     * Author: fudaoji<fdj@kuryun.cn>
     */
    private function massDel(){
        $check = $this->request->checkToken('__token__');
        if (false === $check) {
            fm_set_msg(lng("Invalid Token."), 'danger');
        }
        $path = FM_ROOT_PATH;
        if (FM_PATH != '') {
            $path .= '/' . FM_PATH;
        }

        $params = input();
        $errors = 0;
        $files = $params['file'];
        if (is_array($files) && count($files)) {
            foreach ($files as $f) {
                if ($f != '') {
                    $new_path = $path . '/' . $f;
                    if (!fm_rdelete($new_path)) {
                        $errors++;
                    }
                }
            }
            if ($errors == 0) {
                fm_set_msg(lng('Selected files and folder deleted'));
            } else {
                fm_set_msg(lng('Error while deleting items'), 'danger');
            }
        } else {
            fm_set_msg(lng('Nothing selected'), 'warning');
        }

        $this->redirect($this->url('index', ['p' => FM_PATH]));

        // Mass deleting
        /*if (isset($_POST['group'], $_POST['delete'], $_POST['token']) && !FM_READONLY) {

        }*/
    }

    /**
     * upload
     * @return mixed|void
     * Author: fudaoji<fdj@kuryun.cn>
     */
    public function upload(){
        // Allowed file extensions for create and rename files
        // e.g. 'txt,html,css,js'
        $allowed_file_extensions = '';

        // Allowed file extensions for upload files
        // e.g. 'gif,png,jpg,html,txt'
        $allowed_upload_extensions = '';
        defined('FM_FILE_EXTENSION') || define('FM_FILE_EXTENSION', $allowed_file_extensions);
        defined('FM_UPLOAD_EXTENSION') || define('FM_UPLOAD_EXTENSION', $allowed_upload_extensions);

        if(!empty($_FILES)){ //文件上传
            $check = $this->request->checkToken('__token__');
            if (false === $check) {
                $response = ['status' => 'error','info' => lng("Invalid Token.")];
                echo json_encode($response); exit();
            }

            $params = input();
            $chunkIndex = $params['dzchunkindex'];
            $chunkTotal = $params['dztotalchunkcount'];
            $fullPathInput = fm_clean_path($params['fullpath']);

            $f = $_FILES;
            $path = FM_ROOT_PATH;
            $ds = DIRECTORY_SEPARATOR;
            if (FM_PATH != '') {
                $path .= '/' . FM_PATH;
            }

            $errors = 0;
            $uploads = 0;
            $allowed = (FM_UPLOAD_EXTENSION) ? explode(',', FM_UPLOAD_EXTENSION) : false;
            $response = ['status' => 'error', 'info'   => lng('Oops! Try again')];

            $filename = $f['file']['name'];
            $tmp_name = $f['file']['tmp_name'];
            $ext = pathinfo($filename, PATHINFO_FILENAME) != '' ? strtolower(pathinfo($filename, PATHINFO_EXTENSION)) : '';
            $isFileAllowed = ($allowed) ? in_array($ext, $allowed) : true;

            if(!fm_isvalid_filename($filename) && !fm_isvalid_filename($fullPathInput)) {
                $response =['status' => 'error', 'info' => lng("Invalid File name!")];
                echo json_encode($response); exit();
            }

            $targetPath = $path . $ds;
            if ( is_writable($targetPath) ) {
                $fullPath = $path . '/' . basename($fullPathInput);
                $folder = substr($fullPath, 0, strrpos($fullPath, "/"));

                if (!is_dir($folder)) {
                    $old = umask(0);
                    mkdir($folder, 0777, true);
                    umask($old);
                }

                if (empty($f['file']['error']) && !empty($tmp_name) && $tmp_name != 'none' && $isFileAllowed) {
                    if ($chunkTotal){
                        $out = @fopen("{$fullPath}.part", $chunkIndex == 0 ? "wb" : "ab");
                        if ($out) {
                            $in = @fopen($tmp_name, "rb");
                            if ($in) {
                                if (PHP_VERSION_ID < 80009) {
                                    // workaround https://bugs.php.net/bug.php?id=81145
                                    while (!feof($in)) { fwrite($out, fread($in, 4096)); }
                                } else {
                                    stream_copy_to_stream($in, $out);
                                }
                                $response = array (
                                    'status'    => 'success',
                                    'info' => lng("file upload successful")
                                );
                            } else {
                                $response = array (
                                    'status'    => 'error',
                                    'info' => lng("failed to open output stream"),
                                    'errorDetails' => error_get_last()
                                );
                            }
                            @fclose($in);
                            @fclose($out);
                            @unlink($tmp_name);

                            $response = array (
                                'status'    => 'success',
                                'info' => lng("file upload successful")
                            );
                        } else {
                            $response = array (
                                'status'    => 'error',
                                'info' => lng("failed to open output stream")
                            );
                        }

                        if ($chunkIndex == $chunkTotal - 1) {
                            if (file_exists ($fullPath)) {
                                $ext_1 = $ext ? '.'.$ext : '';
                                $fullPathTarget = $path . '/' . basename($fullPathInput, $ext_1) .'_'. date('ymdHis'). $ext_1;
                            } else {
                                $fullPathTarget = $fullPath;
                            }
                            rename("{$fullPath}.part", $fullPathTarget);
                        }

                    } else if (move_uploaded_file($tmp_name, $fullPath)) {
                        // Be sure that the file has been uploaded
                        if ( file_exists($fullPath) ) {
                            $response = array (
                                'status'    => 'success',
                                'info' => lng("file upload successful")
                            );
                        } else {
                            $response = array (
                                'status' => 'error',
                                'info'   => lng('Couldn\'t upload the requested file.')
                            );
                        }
                    } else {
                        $response = array (
                            'status'    => 'error',
                            'info'      => lng("Error while uploading files. Uploaded files $uploads"),
                        );
                    }
                }
            } else {
                $response = array (
                    'status' => 'error',
                    'info'   => lng('The specified folder for upload isn\'t writeable.')
                );
            }
            // Return the response
            echo json_encode($response);
            exit();
        }elseif (!empty($uploadurl = input('uploadurl'))){
            $path = FM_ROOT_PATH;
            if (FM_PATH != '') {
                $path .= '/' . FM_PATH;
            }
            function event_callback ($message, $callback = null) {
                echo json_encode($message);
            }

            function get_file_path ($path, $fileinfo, $temp_file) {
                return $path."/".basename($fileinfo->name);
            }

            $url = preg_match("|^http(s)?://.+$|", stripslashes($uploadurl)) ? stripslashes($uploadurl) : null;

            //prevent 127.* domain and known ports
            $domain = parse_url($url, PHP_URL_HOST);
            $port = parse_url($url, PHP_URL_PORT);
            $knownPorts = [22, 23, 25, 3306];

            if (preg_match("/^localhost$|^127(?:\.[0-9]+){0,2}\.[0-9]+$|^(?:0*\:)*?:?0*1$/i", $domain) || in_array($port, $knownPorts)) {
                $err = array("message" => lng("URL is not allowed"));
                event_callback(array("fail" => $err));
                exit();
            }

            $use_curl = false;
            $temp_file = tempnam(sys_get_temp_dir(), "upload-");
            $fileinfo = new \stdClass();
            $fileinfo->name = trim(basename($url), ".\x00..\x20");

            $allowed = (FM_UPLOAD_EXTENSION) ? explode(',', FM_UPLOAD_EXTENSION) : false;
            $ext = strtolower(pathinfo($fileinfo->name, PATHINFO_EXTENSION));
            $isFileAllowed = ($allowed) ? in_array($ext, $allowed) : true;

            $err = false;

            if(!$isFileAllowed) {
                $err = array("message" => lng("File extension is not allowed"));
                event_callback(array("fail" => $err));
                exit();
            }

            if (!$url) {
                $success = false;
            } else if ($use_curl) {
                @$fp = fopen($temp_file, "w");
                @$ch = curl_init($url);
                curl_setopt($ch, CURLOPT_NOPROGRESS, false );
                curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
                curl_setopt($ch, CURLOPT_FILE, $fp);
                @$success = curl_exec($ch);
                $curl_info = curl_getinfo($ch);
                if (!$success) {
                    $err = array("message" => curl_error($ch));
                }
                @curl_close($ch);
                fclose($fp);
                $fileinfo->size = $curl_info["size_download"];
                $fileinfo->type = $curl_info["content_type"];
            } else {
                $ctx = stream_context_create();
                @$success = copy($url, $temp_file, $ctx);
                if (!$success) {
                    $err = error_get_last();
                }
            }
            if ($success) {
                $success = rename($temp_file, strtok(get_file_path($path, $fileinfo, $temp_file), '?'));
            }
            if ($success) {
                event_callback(array("done" => $fileinfo));
            } else {
                unlink($temp_file);
                if (!$err) {
                    $err = array("message" => lng("Invalid url parameter"));
                }
                event_callback(array("fail" => $err));
            }
            exit;
        }
        $ext_config = '';
        $extArr = explode(',', FM_UPLOAD_EXTENSION);
        if(FM_UPLOAD_EXTENSION && $extArr) {
            array_walk($extArr, function(&$x) {$x = ".$x";});
            $ext_config = implode(',', $extArr);
        }
        $upload_chunk_size = 2000000;
        $assign = compact('ext_config', 'upload_chunk_size');
        return $this->show($assign);
    }

    /**
     * download
     * Author: fudaoji<fdj@kuryun.cn>
     */
    public function download(){
        $check = $this->request->checkToken('__token__');
        if (false === $check) {
            fm_set_msg(lng("Invalid Token"), 'danger');
        }

        $params = input();
        $dl = urldecode($params['dl']);
        $dl = fm_clean_path($dl);
        $dl = str_replace('/', '', $dl);
        $path = FM_ROOT_PATH;
        if (FM_PATH != '') {
            $path .= '/' . FM_PATH;
        }
        if ($dl != '' && is_file($path . '/' . $dl)) {
            fm_download_file($path . '/' . $dl, $dl, 1024);
            exit;
        }

        fm_set_msg(lng('File not found'), 'danger');
        $this->redirect($this->url('index', ['p' => FM_PATH]));
    }

    /**
     * 复制\移动文件
     * @return mixed|void
     * Author: fudaoji<fdj@kuryun.cn>
     */
    public function copy(){
        $copy = input('copy', '');
        $copy = fm_clean_path($copy);

        if ($copy == '' || !file_exists(FM_ROOT_PATH . '/' . $copy)) {
            fm_set_msg(lng('File not found'), 'danger');
            $this->redirect($this->url('index', ['p' => urlencode(FM_PATH)]));
        }
        $finish = input('finish', 0);
        $move = input('move', 0);
        if($finish){
            // abs path from
            $from = FM_ROOT_PATH . '/' . $copy;
            // abs path to
            $dest = FM_ROOT_PATH;
            if (FM_PATH != '') {
                $dest .= '/' . FM_PATH;
            }
            $dest .= '/' . basename($from);
            // move?
            $move = fm_clean_path(urldecode($move));
            // copy/move/duplicate
            if ($from != $dest) {
                $msg_from = trim(FM_PATH . '/' . basename($from), '/');
                if ($move) { // Move and to != from so just perform move
                    $rename = fm_rename($from, $dest);
                    if ($rename) {
                        fm_set_msg(sprintf(lng('Moved from').' <b>%s</b> '.lng('to').' <b>%s</b>', fm_enc($copy), fm_enc($msg_from)));
                    } elseif ($rename === null) {
                        fm_set_msg(lng('File or folder with this path already exists'), 'warning');
                    } else {
                        fm_set_msg(sprintf(lng('Error while moving from').' <b>%s</b> '.lng('to').' <b>%s</b>', fm_enc($copy), fm_enc($msg_from)), 'danger');
                    }
                } else { // Not move and to != from so copy with original name
                    if (fm_rcopy($from, $dest)) {
                        fm_set_msg(sprintf(lng('Copied from').' <b>%s</b> '.lng('to').' <b>%s</b>', fm_enc($copy), fm_enc($msg_from)));
                    } else {
                        fm_set_msg(sprintf(lng('Error while copying from').' <b>%s</b> '.lng('to').' <b>%s</b>', fm_enc($copy), fm_enc($msg_from)), 'danger');
                    }
                }
            } else {
                if (!$move){ //Not move and to = from so duplicate
                    $msg_from = trim(FM_PATH . '/' . basename($from), '/');
                    $fn_parts = pathinfo($from);
                    $extension_suffix = '';
                    if(!is_dir($from)){
                        $extension_suffix = '.'.$fn_parts['extension'];
                    }
                    //Create new name for duplicate
                    $fn_duplicate = $fn_parts['dirname'].'/'.$fn_parts['filename'].'-'.date('YmdHis').$extension_suffix;
                    $loop_count = 0;
                    $max_loop = 1000;
                    // Check if a file with the duplicate name already exists, if so, make new name (edge case...)
                    while(file_exists($fn_duplicate) & $loop_count < $max_loop){
                        $fn_parts = pathinfo($fn_duplicate);
                        $fn_duplicate = $fn_parts['dirname'].'/'.$fn_parts['filename'].'-copy'.$extension_suffix;
                        $loop_count++;
                    }
                    if (fm_rcopy($from, $fn_duplicate, False)) {
                        fm_set_msg(sprintf('Copyied from <b>%s</b> to <b>%s</b>', fm_enc($copy), fm_enc($fn_duplicate)));
                    } else {
                        fm_set_msg(sprintf('Error while copying from <b>%s</b> to <b>%s</b>', fm_enc($copy), fm_enc($fn_duplicate)), 'danger');
                    }
                } else{
                    fm_set_msg(lng('Paths must be not equal'), 'warning');
                }
            }
            $this->redirect($this->url('index', ['p' => FM_PATH]));
        }
        $assign = compact('copy');
        return $this->show($assign);
    }

    /**
     * rename file / folder
     * Author: fudaoji<fdj@kuryun.cn>
     */
    function rename()
    {
        $check = $this->request->checkToken('__token__');
        if (false === $check) {
            fm_set_msg(lng("Invalid Token."), 'danger');
        }
        $params = input();
        if (empty($params['rename_from']) || empty($params['rename_to'])) {
            fm_set_msg(lng('Invalid file or folder name'), 'danger');
        }

        // old name
        $old = urldecode($params['rename_from']);
        $old = fm_clean_path($old);
        $old = str_replace('/', '', $old);
        // new name
        $new = urldecode($params['rename_to']);
        $new = fm_clean_path(strip_tags($new));
        $new = str_replace('/', '', $new);
        // path
        $path = FM_ROOT_PATH;
        if (FM_PATH != '') {
            $path .= '/' . FM_PATH;
        }
        // rename
        if (fm_isvalid_filename($new) && $old != '' && $new != '') {
            if (fm_rename($path . '/' . $old, $path . '/' . $new)) {
                fm_set_msg(sprintf(lng('Renamed from').' <b>%s</b> '. lng('to').' <b>%s</b>', fm_enc($old), fm_enc($new)));
            } else {
                fm_set_msg(sprintf(lng('Error while renaming from').' <b>%s</b> '. lng('to').' <b>%s</b>', fm_enc($old), fm_enc($new)), 'danger');
            }
        } else {
            fm_set_msg(lng('Invalid characters in file name'), 'danger');
        }
        $this->redirect($this->url('index', ['p' => FM_PATH]));
    }

    /**
     * Delete file / folder
     * Author: fudaoji<fdj@kuryun.cn>
     */
    function delFile() {
        $check = $this->request->checkToken('__token__');
        if (false === $check) {
            fm_set_msg(lng("Invalid Token"), 'danger');
        }

        $params = input();
        if (empty($params['del'])) {
            fm_set_msg(lng('Invalid file or folder name'), 'danger');
        }

        $del = str_replace('/', '', fm_clean_path($params['del']));
        if ($del != '' && $del != '..' && $del != '.') {
            $path = FM_ROOT_PATH;
            if (FM_PATH != '') {
                $path .= '/' . FM_PATH;
            }
            $is_dir = is_dir($path . '/' . $del);
            if (fm_rdelete($path . '/' . $del)) {
                $msg = $is_dir ? lng('Folder') . ' <b>%s</b> ' . lng('Deleted') : lng('File') . ' <b>%s</b> ' . lng('Deleted');
                fm_set_msg(sprintf($msg, fm_enc($del)));
            } else {
                $msg = $is_dir ? lng('Folder') . ' <b>%s</b> ' . lng('not deleted') : lng('File') . ' <b>%s</b> ' . lng('not deleted');
                fm_set_msg(sprintf($msg, fm_enc($del)), 'danger');
            }
        } else {
            fm_set_msg(lng('Invalid file or folder name'), 'danger');
        }
        $this->redirect($this->url('index', ['p' => FM_PATH]));
    }

    /**
     * 文件预览
     * Author: fudaoji<fdj@kuryun.cn>
     */
    function preview(){
        if(! $path = input('path', '')){
            echo "";
            exit;
        }
        if(!is_file($path) || !is_readable($path)){
            echo "";
            exit;
        }
        $file = file_get_contents($path);
        $type = input('type', 'image/jpg');
        /*$content_type = [
            'image' => 'image/jpg',
            'audio' => 'audio/mp3',
            'video' => 'audio/mp4',
            'pdf' => 'application/pdf',
            'xlx' => 'application/vnd.ms-excel',
            'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
        ];*/
        header('Content-Type: ' . $type);
        echo  $file;
        exit;
    }
}